Skip to content

Latest commit

 

History

History
153 lines (118 loc) · 7.72 KB

File metadata and controls

153 lines (118 loc) · 7.72 KB

Memory Management in Swift

Table Of Contents

  1. Memory Allocation Types
    1. Stack: Static Memory Allocation
    2. Heap: Dynamic Memory Allocation
    3. Tricky Examples
      1. Struct Contains a Property of a Class Type
      2. Class Contains a Property of a Struct
      3. Closure Captures a Value Type
  2. ARC - Automatic Reference Counting
    1. What Is ARC?
    2. Retain Cycles
    3. How To Avoid Retain Cycles
  3. Method Dispatch
  4. Copy-on-write Pattern
  5. References

Memory Allocation Types

Stack: Static Memory Allocation

  • The stack is static memory allocation.
  • Objects allocated on the stack have their memory allocated at compile time.
    • Amount of memory required can be calculated at compile time.
  • The stack deals with value types:
    • Basic value types: structs, arrays, dictionaries, enums, tuples, strings, ints, bools, ...
    • Exception 1: a value type as a class property:
      • If a value type is a property of a class, then it's stored on the heap.
    • Exception 2: a value type captured by a closure:
      • If a value type is captured in a closure, then that value will be copied to the heap so that it's still available by the time the closure is executed.
  • The stack is efficient for inserting and removing items.
    • Time complexity of O(1) for insertion, deletion, and peak operations.
    • The cost of allocation and deallocation is low.
  • The stack is LIFO (Last In First Out) data structure.
  • Each thread has its own stack.
    • This means objects within it are thread-safe.
      • No other thread can access that stack, ensuring the safety of value types within its enclosure context.
    • Global access is not possible.

Heap: Dynamic Memory Allocation

  • The heap is dynamic memory allocation.
  • Objects allocated on the heap have their memory allocated at run time.
  • The heap deals with reference types:
    • Classes, actors and closures are stored in the heap.
  • Swift utilizes ARC to automatically clean up memory and remove unused objects.
  • Disadvantages of using the heap:
    • More complex data structure than a stack and slower than a stack.
      • Time complexity: Insertion and deletion are in O(log n), and peak (min/max) is O(1) if you're using a min/max heap.
    • Classes can cause memory leaks.
    • Thread unsafety: The heap is global memory.
      • This can lead to thread unsafety when multiple threads access heap objects simultaneously.

Tricky Examples - Value Types in The Heap

  • Swift guarantee that:

    • The value type will always have the value type features
    • The reference type will always have reference type features
  • Swift does not guarantee that:

    • The exact place in memory (heap vs stack).

Struct Contains a Property of a Class Type

  • The struct will be stored in the stack.
  • The class property will be stored in the heap

Class Contains a Property of a Struct

  • The struct will be stored in the heap with all the other fields of the class.

Closure Captures a Value Type

  • The closure with all captured value types will be stored in the heap.

ARC - Automatic Reference Counting

What Is Automatic Reference Counting (ARC)?

  • What is ARC?
    • ARC is a memory management feature of reference types.
    • The goal of ARC is to know which instances (e.g. an instance of a class) can be removed from memory, to free up space.
    • ARC only applies to reference types.
  • What are benefits of using ARC?
    • Helps to avoid memory leaks.
    • You can focus on writing code, not on the manual memory management.
  • When does ARC operate?
    • ARC operates during compile time.
    • This contrasts with a garbage collector, which manages memory at runtime.
  • How does ARC work?
    • Every object in Swift has a property called the retain count.
      • It represents the number of owners for a particular object.
      • When the retain count is greater than zero, the object is kept in memory.
      • When the retain count reaches zero, the object is removed from memory.

Class vs Instance vs Object vs Reference

  • Class: structure, a "template" that is used to create objects; a blueprint for a house design.
  • Object: all the houses built from that blueprint are objects of that class.
  • Instance: a given house is an instance.
  • Reference: an address of an instance.

Retain Cycles

  • A retain cycle is if two class instances hold a strong reference to each other, such that each instance keeps the other alive in memory.
  • It is common issue:
    • When dealing with parent-child relationships between classes when two class instance properties hold a strong reference to each other.
    • When you assign an escaping closure to a property of a class instance, and the body of that closure captures self.

How To Avoid Retain Cycles

To avoid retain cycles:

  1. In parent-child relationships between classes use:
  • Weak references (weak keywords)
    • Doesn't keep a strong hold on the instance if refers to.
    • Are optional and become nil when the instance that it refers to is deallocated.
  • Unowned references (unowned keywords).
    • Doesn't keep a strong hold on the instance if refers to.
    • Are not optional and are always considered to have a non-nil.
    • The other instance has a longer life time, the instance cannot become nil.
    • Use an unowned reference only when you are sure that the reference always refers to an instance that has not been deallocated.
    • If you try to access the value of an unowned reference after that instance has been deallocated, you'll get a runtime error.
  1. In closures:
  • Use closure capture list.

Method Dispatch

Method Dispatch is an algorithm that finds the invoked method in memory before it gets executed. there are three types of Method Dispatch:

  • Static Dispatch (Direct Dispatch).
  • Dynamic Dispatch (Table Dispatch).
  • Message Dispatch.

There are few ways to reduce Dynamic Dispatch (using final and private keywords, enabling Whole Module Optimization).

More about Method Dispatch you can find here.

Copy-on-write Pattern

  • The Copy-on-write Pattern is used with value types (e.g. structs, arrays, dictionaries, enums and tuples).
  • Every time you use the value type in your code, it is potentially a new value type in memory.
    • Swift copy that new value type to a new memory address only if you modify it.
    • It called copy-on-write pattern. It's a memory optimization.

References